﻿Option Compare Text

Public Class frmMain

    Const UriList As String = "https://gy-crack-suite.googlecode.com/svn/release/URI.dat"
    Friend WithEvents Downloader As New Net.WebClient()
    Dim TargetUrl As String
    Dim TempFile As String
    Dim SaveLocation As String
    Dim SHA512 As New Security.Cryptography.SHA512Managed
    Dim FileList As System.ComponentModel.BindingList(Of GYCrack.FileInfo)
    Dim DownloadManager As New TaskDelegate(AddressOf Download_DoWork)
    Delegate Sub TaskDelegate(Args As Object())

    Private Sub frmMain_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If (e.CloseReason = CloseReason.UserClosing) AndAlso ((Command.Length > 0) OrElse (MsgBox("下载尚未完成，您确定要退出吗？", MsgBoxStyle.YesNo Or MsgBoxStyle.Exclamation, "未完成") = MsgBoxResult.No)) Then e.Cancel = True
    End Sub

    Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If Command.Length = 0 Then
Init:
            MsgBox("请选择程序安装位置，建议您安装在未安装还原系统的分区。", MsgBoxStyle.Question Or MsgBoxStyle.OkOnly, "Download & Install")
            Do While DialogSave.ShowDialog(Me) = Windows.Forms.DialogResult.Cancel
                If MsgBox("您确定要取消下载吗？", MsgBoxStyle.Exclamation Or MsgBoxStyle.YesNo, "Cancel Download") = MsgBoxResult.Yes Then End
            Loop
            SaveLocation = DialogSave.SelectedPath
            If Not My.Computer.FileSystem.DirectoryExists(SaveLocation) Then
                If MsgBox("该文件夹不存在，您要创建吗？", MsgBoxStyle.Exclamation Or MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                    My.Computer.FileSystem.CreateDirectory(SaveLocation)
                Else
                    GoTo Init
                End If
            End If
            If (My.Computer.FileSystem.GetDirectoryInfo(SaveLocation).GetDirectories.Length > 0 OrElse _
            My.Computer.FileSystem.GetDirectoryInfo(SaveLocation).GetFiles.Length > 0) AndAlso _
            MsgBox("该文件夹并不是空的，文件可能被覆盖或删除。建议您创建一个新的空文件夹安装。您确定要继续安装在当前文件夹吗？", MsgBoxStyle.Exclamation Or MsgBoxStyle.YesNo) = MsgBoxResult.No Then GoTo Init
        Else
            If Not My.Computer.Network.IsAvailable Then
                Process.Start(Command() & "\GYCrack.exe", "-offline")
                End
            End If
            SaveLocation = Command()
        End If
        DownloadManager.BeginInvoke({1, 0}, Nothing, Nothing)
        RefreshTimer.Enabled = True
    End Sub

    Private Sub Download_DoWork(Args As Object())
        Dim Phase As Integer = CInt(Args(0))
        Dim Index As Integer = CInt(Args(1))
        Select Case Phase
            Case 1
                Dim TimeoutServer As New TaskDelegate(AddressOf TimeoutHandler)
                TimeoutServer.BeginInvoke(Nothing, Nothing, Nothing)
                SetText({"Retrieving file list ..."})
                TempFile = My.Computer.FileSystem.GetTempFileName
                Downloader.DownloadFileAsync(New Uri(UriList), TempFile, New Object() {2, 0})
            Case 2
                Select Case Index
                    Case 0
                        SetText({"Processing file list ..."})
                        Try
                            Dim Serializer As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
                            Dim FS As New IO.FileStream(TempFile, IO.FileMode.Open, IO.FileAccess.Read)
                            FileList = CType(Serializer.Deserialize(FS), System.ComponentModel.BindingList(Of FileInfo))
                            FS.Close()
                        Catch ex As Exception
                            Downloader.DownloadFileAsync(New Uri(UriList), TempFile, New Object() {2, 0})
                            Exit Sub
                        End Try
                        SetText({"Terminating running instance ..."})
                        Dim Processes = Process.GetProcesses
                        SetProgress({0, 0, Processes.Length})
                        For i = 0 To Processes.Length - 1
                            Dim b = Processes(i)
                            Dim Checksum As Byte()
                            Try
                                Checksum = SHA512.ComputeHash(My.Computer.FileSystem.ReadAllBytes(b.StartInfo.FileName))
                            Catch ex As Exception
                                Continue For
                            End Try
                            For Each c In FileList
                                If IsByteArrayEqual(c.Checksum, Checksum) Then
                                    b.Kill()
                                    Exit For
                                End If
                            Next
                            SetProgress({i + 1})
                        Next
                        SetText({"Preparing update ..."})
                        Dim a = My.Computer.FileSystem.GetFiles(SaveLocation)
                        SetProgress({0, 0, a.Count})
                        For i = 0 To a.Count - 1
                            Dim b = a(i)
                            Dim Checksum As Byte()
                            Try
                                Checksum = SHA512.ComputeHash(My.Computer.FileSystem.ReadAllBytes(b))
                            Catch ex As Exception
                                Continue For
                            End Try
                            For Each c In FileList
                                If c.InstallBehavior = FileInfo.InstallBehaviors.Deleted Then
                                    If IsByteArrayEqual(c.Checksum, Checksum) Then
                                        My.Computer.FileSystem.DeleteFile(b)
                                        Exit For
                                    End If
                                End If
                            Next
                            SetProgress({i + 1})
                        Next
                        SetText({"Updating ..."})
                        SetProgress({0, 0, FileList.Count})
                        DownloadManager.BeginInvoke({2, 1}, Nothing, Nothing)
                    Case Else
                        Dim Current = FileList(Index - 1)
                        Dim CurrentName As String = SaveLocation & "\" & Current.Name
                        If Not (My.Computer.FileSystem.FileExists(CurrentName) AndAlso IsByteArrayEqual(Current.Checksum, SHA512.ComputeHash(My.Computer.FileSystem.ReadAllBytes(CurrentName)))) Then
                            If My.Computer.FileSystem.FileExists(CurrentName) Then My.Computer.FileSystem.DeleteFile(CurrentName)
                            Downloader.DownloadFileAsync(New Uri(Current.URL), CurrentName, New Object() {2, Index})
                        ElseIf Index >= FileList.Count Then
                            Process.Start(SaveLocation & "\GYCrack.exe", "-online")
                            DownloadManager.BeginInvoke({3, 0}, Nothing, Nothing)
                        Else
                            SetProgress({Index})
                            DownloadManager.BeginInvoke({2, Index + 1}, Nothing, Nothing)
                        End If
                End Select
            Case 3
                CommencePhase3({})
                Dim CheckList As New List(Of Byte())
                For Each c In FileList
                    If c.InstallBehavior = FileInfo.InstallBehaviors.SearchAndDelete Then CheckList.Add(c.Checksum)
                Next
                If CheckList.Count = 0 Then End
                For Each Drive In My.Computer.FileSystem.Drives
                    If UCase(Drive.Name) = "C:\" Then Continue For
                    Dim a = My.Computer.FileSystem.GetFiles(Drive.RootDirectory.FullName)
                    For i = 0 To a.Count - 1
                        Dim b = a(i)
                        Dim Checksum As Byte()
                        Try
                            Checksum = SHA512.ComputeHash(My.Computer.FileSystem.ReadAllBytes(b))
                        Catch ex As Exception
                            Continue For
                        End Try
                        For Each c In CheckList
                            If IsByteArrayEqual(c, Checksum) Then
                                My.Computer.FileSystem.DeleteFile(b)
                                Exit For
                            End If
                        Next
                    Next
                Next
                End
        End Select
    End Sub

    Private Sub Downloader_DownloadFileCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Handles Downloader.DownloadFileCompleted
        DownloadManager.BeginInvoke(CType(e.UserState, Object()), Nothing, Nothing)
    End Sub

    Private Sub Downloader_DownloadProgressChanged(sender As Object, e As System.Net.DownloadProgressChangedEventArgs) Handles Downloader.DownloadProgressChanged
        SetProgress({e.ProgressPercentage, 0, 100})
        SetText({"Downloading ... (" & e.BytesReceived & "Bytes / " & e.TotalBytesToReceive & "Bytes)"})
    End Sub

    Private Sub RefreshTimer_Tick(sender As Object, e As EventArgs) Handles RefreshTimer.Tick
        Me.Refresh()
    End Sub

    Private Sub SetText(Args As Object())
        If Me.InvokeRequired Then
            Me.Invoke(New TaskDelegate(AddressOf SetText), {Args})
        Else
            txtProgress.Text = CStr(Args(0))
        End If
    End Sub

    Private Sub SetProgress(Args As Object())
        If Me.InvokeRequired Then
            Me.Invoke(New TaskDelegate(AddressOf SetProgress), {Args})
        Else
            If Args.Length >= 3 Then
                Progress.Minimum = CInt(Args(1))
                Progress.Maximum = CInt(Args(2))
            End If
            Progress.Value = CInt(Args(0))
        End If
    End Sub

    Private Sub CommencePhase3(Args As Object())
        If Me.InvokeRequired Then
            Me.Invoke(New TaskDelegate(AddressOf CommencePhase3), {Args})
        Else
            Me.Visible = False
            RefreshTimer.Enabled = False
        End If
    End Sub

    Private Sub TimeoutHandler(Args As Object())
Start:
        Threading.Thread.Sleep(30000)
        Dim Result As MsgBoxResult
        If Command.Length > 0 Then
            Result = MsgBox("检测到下载时间过长，选择""终止""开启离线模式，选择""重试""重启下载，选择""忽略""继续当前下载。", MsgBoxStyle.AbortRetryIgnore Or MsgBoxStyle.Exclamation, "Timeout")
        Else
            MsgBox("检测到下载时间过长，选择""重试""重启下载，选择""取消""继续当前下载。", MsgBoxStyle.RetryCancel Or MsgBoxStyle.Exclamation, "Timeout")
        End If
        Select Case Result
            Case MsgBoxResult.Abort
                Process.Start(Command() & "\GYCrack.exe", "-offline")
                End
            Case MsgBoxResult.Retry
                Restart()
            Case MsgBoxResult.Ignore, MsgBoxResult.Cancel
                GoTo Start
        End Select
    End Sub

    Private Function IsByteArrayEqual(ByVal a As Byte(), ByVal b As Byte()) As Boolean
        If a.Length <> b.Length Then Return False
        For i = 0 To a.Length - 1
            If a(i) <> b(i) Then Return False
        Next
        Return True
    End Function

    Public Shared Sub Restart()
        Application.Restart()
    End Sub

End Class